package pattern.gateway.config;

import cn.hutool.core.collection.CollUtil;
import com.alibaba.cloud.nacos.NacosConfigManager;
import com.alibaba.cloud.nacos.NacosConfigProperties;
import com.alibaba.nacos.api.config.ConfigService;
import com.alibaba.nacos.api.config.listener.Listener;
import com.alibaba.nacos.api.exception.NacosException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.concurrent.BasicThreadFactory;
import org.springframework.cloud.gateway.config.GatewayProperties;
import org.springframework.cloud.gateway.event.RefreshRoutesEvent;
import org.springframework.cloud.gateway.route.RouteDefinition;
import org.springframework.cloud.gateway.route.RouteDefinitionRepository;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import org.yaml.snakeyaml.Yaml;
import org.yaml.snakeyaml.constructor.Constructor;
import pattern.utils.CollectionUtil;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.util.List;
import java.util.concurrent.Executor;
import java.util.concurrent.ScheduledThreadPoolExecutor;

/**
 * gateway route热加载
 * nacos修改gateway路由，动态自动加载刷新路由配置
 * @author Administrator
 * @since 2.0.0.0
 */
@Slf4j
@Configuration
public class RouteAutoRefreshConfiguration implements RouteDefinitionRepository {

    private final ApplicationEventPublisher publisher;

    private final NacosConfigProperties nacosConfigProperties;

    private final ConfigService configService;

    private List<RouteDefinition> routeDefinitionList;

    private static String sourceId;
    private static List<RouteDefinition> routeLists;


    /**
     * 初始化属性
     */
    public RouteAutoRefreshConfiguration(ApplicationEventPublisher publisher, NacosConfigProperties nacosConfigProperties, Environment environment) {
        this.publisher = publisher;
        this.nacosConfigProperties = nacosConfigProperties;
        this.configService = new NacosConfigManager(nacosConfigProperties).getConfigService();
        sourceId = environment.getProperty("gateway.source");
        this.addListener();
    }

    /**
     * 重新加载路由
     */
    @Override
    public Flux<RouteDefinition> getRouteDefinitions() {
        try {
            if(CollectionUtil.isNotEmpty(routeLists)){
                return Flux.fromIterable(routeLists);
            }
            log.info("刷新gateway-route连接");
            var content = configService.getConfig(sourceId, nacosConfigProperties.getGroup(), 5000);
            routeLists = getRouteDefinitions(content);
            routeDefinitionList = routeLists;
            log.info(routeLists.toString());
            return Flux.fromIterable(routeLists);
        } catch (NacosException e) {
            return Flux.fromIterable(CollUtil.newArrayList());
        }
    }

    /**
     * 解析路由
     */
    private List<RouteDefinition> getRouteDefinitions(String content) {
        Yaml yaml = new Yaml(new Constructor(GatewayProperties.class));
        InputStream inputStream = new ByteArrayInputStream(content.getBytes());
        GatewayProperties gatewayProperties = yaml.load(inputStream);
        return gatewayProperties.getRoutes();
    }


    /**
     * naocs修改监听
     */
    public void addListener() {
        try {
            configService.addListener(sourceId, nacosConfigProperties.getGroup(), new Listener() {
                @Override
                public Executor getExecutor() {
                    return new ScheduledThreadPoolExecutor(1,
                            new BasicThreadFactory.Builder().namingPattern("route-listen-pool-%d").daemon(Boolean.TRUE).build());
                }

                @Override
                public void receiveConfigInfo(String configInfo) {
                    if (StringUtils.isNotEmpty(configInfo)) {
                        routeLists.clear();
                        publisher.publishEvent(new RefreshRoutesEvent(this));
                    }
                }
            });
        } catch (NacosException e) {
            log.error("动态路由加载失败");
        }
    }

    public List<RouteDefinition> getRouteDefinitionList() {
        return this.routeDefinitionList;
    }

    @Override
    public Mono<Void> save(Mono<RouteDefinition> route) {
        return null;
    }

    @Override
    public Mono<Void> delete(Mono<String> routeId) {
        return null;
    }
}
